package com.smx.steppingstone.visitors;

import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.util.Collection;
import java.util.Date;
import java.util.Map;

public class AbstractVisitor implements ReflectiveVisitor{
	protected int depth=0;
	protected int maxdepth=0;
	
	public AbstractVisitor(){
		maxdepth=5;
	}
	public AbstractVisitor(int maxdepth){
		this.maxdepth=maxdepth;
	}
	public void visit(Object o) {
		visit(o, null);
	}
	protected void visit(Object o, Object field) {
		if(null == o)return;
		Class<?> cls=o.getClass();
		if(cls==Object.class){
			return;
		}

		String methodName = o.getClass().getName();
		Method executor=null;
		if(Collection.class.isAssignableFrom(o.getClass())){
			methodName="visitCollection";
			cls=Collection.class;
		}else if(Map.class.isAssignableFrom(o.getClass())){
			methodName="visitMap";
			cls=Map.class;
		}else if(Boolean.class.isAssignableFrom(o.getClass())){
			methodName="visitBoolean";
			cls=Boolean.class;
		}else if(Date.class.isAssignableFrom(o.getClass())){
			methodName="visitDate";
			cls=Date.class;
		}
		
		else if(o.getClass().isArray()){
			methodName="visitArray";
			cls=Object[].class;
		}else{
			methodName = "visit"+ methodName.substring(methodName.lastIndexOf('.')+1);						
			boolean isFound=false;
			for(Method m:getClass().getMethods()){
				if(m.getName().equals(methodName)){
					isFound=true;
					break;
				}
			}	
			if(!isFound){
				methodName="visitObject";
				cls=Object.class;
			}
		}
		try {
			Class<?> fieldOrMethodClass=(null==field)?Field.class:field.getClass();
			if(fieldOrMethodClass==Method.class){
				methodName="visitMethod";
			}
			
			/*			
			  c("------ "+depth+"----------");
			  c("-cls=="+cls); 
			  c("-Object=="+o);
			  c("-methodName=="+methodName);			  
			*/
			executor=getClass().getMethod(methodName, new Class[]{ cls, fieldOrMethodClass });
			executor.invoke(this, new Object[]{o, field});
		}catch(Exception e){
			//Do nothing
			//e.printStackTrace();
		}
	}

	public void visitCollection(Collection<?> o, Field f) {
		throw new RuntimeException("Method not implemented");

	}

	public void visitFloat(Float o, Field f) {
		throw new RuntimeException("Method not implemented");

	}

	public void visitInteger(Integer o, Field f) {
		throw new RuntimeException("Method not implemented");

	}

	public void visitMethod(Object o, Method f) {
		throw new RuntimeException("Method not implemented");

	}

	public void visitObject(Object o, Field f) {
		throw new RuntimeException("Method not implemented");
	}

	public void visitString(String o, Field f) {
		throw new RuntimeException("Method not implemented");
	}
	
	public void visitArray(Object[] o, Field f) {
		throw new RuntimeException("Method not implemented");
	}
	
	public void visitMap(Map<?, ?> map, Field f) {
		throw new RuntimeException("Method not implemented");
	}
	public void visitBoolean(Boolean o, Field f) {
		throw new RuntimeException("Method not implemented");
	}
	private static void c(Object o){System.out.println(o);}
	
	public void visitDate(Date o, Field f) {
		throw new RuntimeException("Method not implemented");
	}
	/**
	 * Set maximum depth the object will be instected to
	 * @param maxdepth
	 */
	public void setMaxdepth(int maxdepth) {
		this.maxdepth = maxdepth;
	}

}
